# The main frame of the application.

import wx
import wx.html
import os
import time
import random
import i18n
import javavatask.model as md
from javavatask.storage import getLanguage, setLanguage

# The current version number of the application, we need to change it when we
# deploy it each time.
version = "1.0.2"

icondir = "./resources/icons"
model = md.TaskListModel()

##
# Create menu from specified menu data.
# 
# @param parent: The parent of the menu.
# @param menuData: The menu data which is supported as 
#
def createMenu(parent, menuData):
    menu = wx.Menu()
    for eachLabel, eachStatus, eachHandler in menuData:
        if not eachLabel:
            menu.AppendSeparator()
            continue
        menuItem = menu.Append(-1, eachLabel, eachStatus)
        parent.Bind(wx.EVT_MENU, eachHandler, menuItem)
    return menu

##
# The TaskBarIcon class
#
class MyTaskBarIcon(wx.TaskBarIcon):
    ##
    # Initialize the task bar icon of the operation system.
    #
    def __init__(self, frame):
        wx.TaskBarIcon.__init__(self)
        self.frame = frame
        
        # Setup icon object
        self.SetIcon(frame.icon, _("JavavaTask - Manage tasks by importance and urgent level."))
        
        # Create the popup menu for tack bar icon.
        self.popupMenu = createMenu(self, self.popupMenuData())
        self.Bind(wx.EVT_TASKBAR_LEFT_DCLICK, self.OnLeftDClick)
        self.Bind(wx.EVT_TASKBAR_RIGHT_DOWN, self.OnRightClick)

    def OnLeftDClick(self, evt):
        if not self.frame.IsShown():
            self.frame.Show()
        else:
            self.frame.Hide()

    def OnRightClick(self, evt):
        self.PopupMenu(self.popupMenu)
        
    def popupMenuData(self):
        return ((_("Show JavavaTask"), _("Show the main frame of JavavaTask."), self.showMainFrame),
                (_("Hide JavavaTask"), _("Hide the main frame of JavavaTask."), self.hideMainFrame),
                ("", "", ""),
                (_("About JavavaTask"), _("Introduction of the JavavaTask."), self.frame.OnAbout),
                ("", "", ""),
                (_("Quit"), _("Quit"), self.frame.OnCloseWindow)
                )
    
    def showMainFrame(self, evt):
        if not self.frame.IsShown():
            self.frame.Show()

    def hideMainFrame(self, evt):
        if self.frame.IsShown():
            self.frame.Hide()

class MainFrame(wx.Frame):
    """The main frame of javava task application."""

    def __init__(self):
        wx.Frame.__init__(self, None, -1, _("JavavaTask")+' '+version, 
                          size=(350, 480))
        panel = wx.Panel(self, -1)
        
        # Set icon of the main frame.
        self.icon = wx.Icon(os.path.join(icondir, 'app_icon_16x16.png'), 
                       wx.BITMAP_TYPE_PNG)
        self.SetIcon(self.icon)
        
        inputPanel = InputPanel(panel)
        taskList = TaskListPanel(panel)
        
        box = wx.BoxSizer(wx.VERTICAL)
        box.Add(inputPanel, 0, wx.EXPAND)
        box.Add(taskList, 1, wx.ALL^wx.TOP|wx.EXPAND, border=5)
        panel.SetSizer(box)
        
        self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
        self.createMenuBar()
        
        self.Centre()
        self.taskBarIcon = MyTaskBarIcon(self)

    def menuData(self):
        """Initialize the menu data.
        """
        
        return ((_("&File"),
                    #(_("&History"), _("Query task history."), self.OnHistory),
                    (_("&Options..."), _("Set options in popup dialog."), self.OnOptions),
                    ("", "", ""),
                    (_("&Quit") + "\tCtrl+Q", _("Quit"), self.OnCloseWindow)),
                (_("&Help"),
                    (_("&Help Contents"), _("Help Contents."), self.OnHelpContents),
                     ("", "", ""),
                    (_("&About..."), _("Introduction of the JavavaTask."), self.OnAbout)))

    def createMenuBar(self):
        menuBar = wx.MenuBar()
        for eachMenuData in self.menuData():
            menuLabel = eachMenuData[0]
            menuItems = eachMenuData[1:]
            menuBar.Append(createMenu(self, menuItems), menuLabel)
            self.SetMenuBar(menuBar)
    
    def OnHistory(self, event):
        pass

    def OnOptions(self, event):
        labels = [_("English"), _("Simplified Chinese"), _("Traditional Chinese")]
        dialog = wx.SingleChoiceDialog(self, 
                                       _("Choose a preferred language, "
                                          "it will be valid after \nrestarting "
                                          "application."),
                                       _("Language Setting"), labels)
        current_language = i18n.load_language_setting()
        
        if current_language:
            dialog.SetSelection(i18n.languages.index(current_language))
        
        if dialog.ShowModal() == wx.ID_OK:
            selected = dialog.GetSelection()
            setLanguage(None, i18n.languages[selected])
        dialog.Destroy()

    def OnCloseWindow(self, event):
        # Remove the task bar icon
        self.taskBarIcon.RemoveIcon()
        self.taskBarIcon.Destroy()
        
        self.Destroy()
        wx.GetApp().ExitMainLoop()

    def OnHelpContents(self, event):
        os.startfile("http://code.google.com/p/javava/wiki/UserGuide_" + 
                     getLanguage())

    def OnAbout(self, event):
        if not self.IsShown():
            self.Show()
            
        dlg = AboutDialog(self)
        dlg.ShowModal()
        dlg.Destroy()

class AboutDialog(wx.Dialog):
    text = '''
<html>
<body bgcolor="#D8F7FF">
<center><table bgcolor="#6100FF" width="100%" cellspacing="0"
cellpadding="0" border="1">
<tr>
<td align="center"><h3><font color="#FFFFFF">JavavaTask ${version}</font></h3></td>
</tr>
</table>
</center>
<p><b>JavavaTask</b> is a small tool for managing your daily tasks, It 
can sort your tasks by importance and urgent level. Please visit 
<font color="#0000FF">http://javava.googlecode.com</font>
</p>
<p><b>JavavaTask</b> is brought to you by <b>Widen</b> and 
<b>Ivan Li</b>, Copyright &copy; 2008.</p>
</body>
</html>
'''

    def __init__(self, parent):
        wx.Dialog.__init__(self, parent, -1, _('About JavavaTask')+' '+version,
                           size=(300, 300))
        html = wx.html.HtmlWindow(self)
        self.text = self.text.replace('${version}', version)
        html.SetPage(self.text)
        button = wx.Button(self, wx.ID_OK, _("Ok"))
        button.SetDefault()
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(html, 1, wx.EXPAND|wx.ALL, 5)
        sizer.Add(button, 0, wx.ALIGN_CENTER|wx.ALL, 5)
        self.SetSizer(sizer)
        self.Layout()

class InputPanel(wx.Panel):
    """This panel will show a textfield and an 'Add' button. After users 
    input text in the textfield, and press 'Enter' key, or click the 'Add'
    button, it will add a new task into the task list. 
    """
    
    def __init__(self, parent):
        wx.Panel.__init__(self, parent)
        
        # The text field which users can input new task in.
        self.newTaskText = wx.TextCtrl(self, -1, style=wx.TE_PROCESS_ENTER, 
                                       size=(-1, -1))
        self.newTaskText.SetInsertionPoint(0)       
        self.Bind(wx.EVT_TEXT_ENTER, self.OnAdd, self.newTaskText)
        
        # Add button, when users click it, the new task will be added 
        # into the task list.
        bmp = wx.Image(icondir + '/' + _("add_button.png"), 
                       wx.BITMAP_TYPE_PNG).ConvertToBitmap()
        addButton = wx.BitmapButton(self, -1, bmp)
        
        box = wx.BoxSizer(wx.HORIZONTAL)
        box.Add(self.newTaskText, 1, wx.ALL|wx.ALIGN_CENTER_VERTICAL, border=5)
        box.Add(addButton, 0, (wx.ALL^wx.LEFT)|wx.ALIGN_CENTER_VERTICAL, 
                border=5)
        self.SetSizer(box)
        
        self.Bind(wx.EVT_BUTTON, self.OnAdd, addButton)

    def OnAdd(self, event):
        '''Add new task to the model.
        '''
        
        id = time.strftime('%M') + str(random.choice(range(10,99)))
        label = self.newTaskText.GetValue()
        newTask = md.Task(id, time.strftime('%Y%m%d'), label)
        model.addTask(newTask)
        
        self.newTaskText.SetValue('')

class TaskListPanel(wx.Panel):
    
    taskBarList = []
    
    def __init__(self, parent):
        wx.Panel.__init__(self, parent)
        self.SetBackgroundColour('white')
        model.addListener(self.onUpdate)
        
        self.scroll = wx.ScrolledWindow(self, -1)
        self.scroll.SetScrollbars(10, 10, -1, -1)

        self.box = wx.BoxSizer(wx.VERTICAL)
        self.onUpdate(model)
        
        self.scroll.SetSizer(self.box)
        self.scroll.FitInside()
        
        box2 = wx.BoxSizer(wx.VERTICAL)
        box2.Add(self.scroll, 1, wx.TOP|wx.EXPAND, border=5)
        self.SetSizer(box2)
    
    def onUpdate(self, model):
        '''This method is the listener of the model, if the model is updated, 
        it will call this method to update the user interface.
        '''
        
        taskSize = len(model.taskList)
        barSize = len(self.taskBarList)
        
        for i in range(taskSize):
            task = model.taskList[i]
            if i < barSize:
                self.taskBarList[i].updateTask(i, task)
            else:
                taskBar = TaskRow(self.scroll, i, model.taskList[i])
                self.taskBarList.append(taskBar)
                self.box.Add(self.taskBarList[i], 0, wx.TOP|wx.EXPAND, 
                             border=2)
        
        if taskSize < barSize:
            for removeIndex in range(taskSize, barSize):
                self.box.Detach(self.taskBarList[removeIndex])
                self.taskBarList[removeIndex].Destroy()
                del self.taskBarList[removeIndex]

        self.scroll.FitInside()
        self.scroll.Layout()

class TaskRow(wx.Panel):
    """This class will show one task information and operations, task list 
    is made of several task bars.
    """
    
    progress_icons = [os.path.join(icondir, 'done.png'), 
                       os.path.join(icondir, 'inprogress.png')]
    icon_delete = os.path.join(icondir, 'delete.png')
    
    def __init__(self, parent, index, task):
        self.index = index
        self.task = task
        
        wx.Panel.__init__(self, parent)
        self.SetBackgroundColour('white')

        # Create buttons
        self.btnProgress = self.newIconBtn(self.progress_icons[0],
                                           self.onProgress,
                                           self.onProgress)
        self.btnImportance = self.newIconBtn(self.getImpIcon(1),
                                             self.OnImportance1,
                                             self.OnImportance2)
        self.btnUrgency = self.newIconBtn(self.getUrgIcon(1),
                                          self.OnUrgency1,
                                          self.OnUrgency2)
        self.btnDelete = self.newIconBtn(self.icon_delete, self.OnDelete)
        
        self.taskLabel = TaskLabel(self, self.task.label)
        self.taskLabel.Bind(wx.EVT_LEFT_DCLICK, self.OnDoubleClick)
        self.taskLabel.Bind(wx.EVT_RIGHT_UP, self.OnRightUp)

        self.box = wx.BoxSizer(wx.HORIZONTAL)
        self.box.Add(self.btnProgress, 0, wx.LEFT|wx.ALIGN_CENTER_VERTICAL, border=5)
        self.box.Add(self.btnImportance, 0, wx.LEFT|wx.ALIGN_CENTER_VERTICAL, border=2)
        self.box.Add(self.btnUrgency, 0, wx.LEFT|wx.ALIGN_CENTER_VERTICAL, border=2)
        self.box.Add(self.taskLabel, 1, wx.LEFT|wx.ALIGN_CENTER_VERTICAL, border=2)
        self.box.Add(self.btnDelete, 0, wx.LEFT|wx.RIGHT|wx.ALIGN_CENTER_VERTICAL, border=5)
        
        self.SetSizer(self.box)
        self.Layout()
        
        self.updateTask(index, self.task)
    
    def getImpIcon(self, index, disabled=False):
        "Get importance icon by given index and disabled status."
        
        name = 'important_' + str(index)
        if disabled:
            name += '_disabled'
        name += '.png'
        return os.path.join(icondir, name)

    def getUrgIcon(self, index, disabled=False):
        "Get urgency icon by given index and disabled status."
        
        name = 'urgency_' + str(index)
        if disabled:
            name += '_disabled'
        name += '.png'
        return os.path.join(icondir, name)
    
    def updateTask(self, index, task):
        self.index = index
        self.task = task
        
        progress = int(self.task.status[0])
        importance = int(self.task.status[1])
        urgency = int(self.task.status[2])
        disabled = (progress == 0)
        
        bmp = wx.Image(self.progress_icons[progress], 
                       wx.BITMAP_TYPE_PNG).ConvertToBitmap()
        self.btnProgress.SetBitmapLabel(bmp)
                
        bmp = wx.Image(self.getImpIcon(importance, disabled),
                       wx.BITMAP_TYPE_PNG).ConvertToBitmap()
        bmp.SetSize((16, 16))
        self.btnImportance.SetBitmapLabel(bmp)
                
        bmp = wx.Image(self.getUrgIcon(urgency, disabled),
                       wx.BITMAP_TYPE_PNG).ConvertToBitmap()
        bmp.SetSize((16, 16))
        self.btnUrgency.SetBitmapLabel(bmp)
        
        if self.taskLabel:
            self.taskLabel.setText(self.task.label)
        elif self.taskText:
            self.taskText.SetValue(self.task.label)
    
    def newIconBtn(self, icon, handler, rightUpHander=None):
        bmp = wx.Image(icon, wx.BITMAP_TYPE_PNG).ConvertToBitmap()
        btn = wx.BitmapButton(self, -1, bmp, style=0)
        btn.SetBackgroundColour('white')
        
        # Bind the handler
        self.Bind(wx.EVT_BUTTON, handler, btn)
        if rightUpHander: btn.Bind(wx.EVT_RIGHT_UP, rightUpHander)
        return btn
    
    def OnDoubleClick(self, evt):
        # If the current task is finished, the user can't change the 
        # label of the task.
        if int(self.task.status[0]) == 0: return
        
        self.taskText = wx.TextCtrl(self, -1, self.task.label, 
                                    style=wx.TE_PROCESS_ENTER, size=(-1, 20))
        self.taskText.SetFocus()
        self.taskText.SetInsertionPointEnd()
        self.Bind(wx.EVT_TEXT_ENTER, self.OnTextEnter, self.taskText)
        
        self.box.Detach(self.taskLabel)
        self.taskLabel.Destroy()
                
        self.box.Insert(3, self.taskText, 1, wx.LEFT|wx.ALIGN_CENTER_VERTICAL, border=2)
        self.Layout()
    
    def OnRightUp(self, evt):
        "When user right clicks on the task label, the tasks will be sorted."
        
        model.sortTasks()
        
    def OnTextEnter(self, evt):
        self.task.label = self.taskText.GetValue()
        self.taskLabel = TaskLabel(self, self.task.label)
        self.taskLabel.Bind(wx.EVT_LEFT_DCLICK, self.OnDoubleClick)
        
        self.box.Detach(self.taskText)
        self.taskText.Destroy()

        self.box.Insert(3, self.taskLabel, 1, 
                        wx.LEFT|wx.ALIGN_CENTER_VERTICAL, border=2)
        self.Layout()
        
        model.setTaskLabel(self.index, self.task.label)

    def onProgress(self, evt):
        # Left click or right click on the progress button.
        progress = str((int(self.task.status[0]) + 1) % 2)
        status = progress + self.task.status[1:]
        model.setTaskStatus(self.index, status)

    def OnImportance1(self, evt):
        "Left click on the importance button."
        
        # If the current task is finished, the user can't change the 
        # importance and urgent level of the task.
        if int(self.task.status[0]) == 0: return
        
        importance = str(int(self.task.status[1]) % 5 + 1)
        status = self.task.status[0] + importance + self.task.status[2]
        model.setTaskStatus(self.index, status)

    def OnImportance2(self, evt):
        "Right click on the importance button."
        
        # If the current task is finished, the user can't change the 
        # importance and urgent level of the task.
        if int(self.task.status[0]) == 0: return
        
        intValue = int(self.task.status[1])
        importance = str((intValue + 5 - 2) % 5 + 1)
        status = self.task.status[0] + importance + self.task.status[2]
        model.setTaskStatus(self.index, status)

    def OnUrgency1(self, evt):
        "Left click on the urgent level button."
        
        # If the current task is finished, the user can't change the 
        # importance and urgent level of the task.
        if int(self.task.status[0]) == 0: return

        urgency = str(int(self.task.status[2]) % 5 + 1)
        status = self.task.status[0] + self.task.status[1] + urgency
        model.setTaskStatus(self.index, status)

    def OnUrgency2(self, evt):
        "Right click on the urgent level button."
        
        # If the current task is finished, the user can't change the 
        # importance and urgent level of the task.
        if int(self.task.status[0]) == 0: return
        
        intValue = int(self.task.status[2])
        urgency = str((intValue + 5 - 2) % 5 + 1)
        status = self.task.status[0] + self.task.status[1] + urgency
        model.setTaskStatus(self.index, status)

    def OnDelete(self, evt):
        '''When the user clicks on the delete button of each task, it will 
        pop up a dialog for confirmation, if the user clicks 'OK', then 
        delete the task from the model.
        '''
        
        resultCode = wx.MessageBox(_("Are you sure to delete this task?"), _("Delete Confirmation"),
                                   wx.YES_NO | wx.ICON_QUESTION)
        if resultCode == wx.YES:
            model.deleteTask(self.index)

class TaskLabel(wx.Panel):
    """This class will be used to show the text of task, because we need to
    receive the mouse events on this widget, so we don't use the StaticText
    widget, and rewrite this widget.
    """
    
    def __init__(self, parent, label):
        wx.Panel.__init__(self, parent)
        self.label = label
        
        self.SetBackgroundColour("white")
        self.labelFont = wx.Font(9, wx.SWISS, wx.NORMAL, wx.NORMAL)
        self.InitBuffer()
        
        self.Bind(wx.EVT_SIZE, self.OnSize)
        self.Bind(wx.EVT_IDLE, self.OnIdle)
        self.Bind(wx.EVT_PAINT, self.OnPaint)
    
    def setText(self, label):
        self.label = label
        self.InitBuffer()
        
        # Causes an EVT_PAINT event to be generated and sent to the window.
        self.Refresh()

    def InitBuffer(self):
        size = self.GetClientSize()
        self.buffer = wx.EmptyBitmap(size.width, size.height)
        dc = wx.BufferedDC(None, self.buffer)
        dc.SetBackground(wx.Brush(self.GetBackgroundColour()))
        dc.Clear()
        
        dc.SetFont(self.labelFont)
        dc.DrawText(self.label, 0, 2)
        self.reInitBuffer = False

    def OnSize(self, event):
        self.reInitBuffer = True

    def OnIdle(self, event):
        if self.reInitBuffer:
            self.InitBuffer()
            self.Refresh(False)

    def OnPaint(self, event):
        dc = wx.BufferedPaintDC(self, self.buffer)

if __name__ == '__main__':
    pass
